<script setup>
// 文档、富文本组件
import {
  ref,
  onMounted,
  watch,
  onBeforeUnmount,
  shallowRef,
  nextTick,
  computed,
  reactive,
} from 'vue'
import { debounce } from '@/utils/tools.ts'
import { getHost, getNoteNameAndIcon, getIconPath } from '@/utils'
import '@wangeditor/editor/dist/css/style.css'
import { Toolbar, Editor } from '@wangeditor/editor-for-vue'
import { SlateEditor, SlateElement, SlateTransforms } from '@wangeditor/editor'
import {
  ToolbarConfigs,
  uploadImageConfig,
} from '@/views/note/components/documentTools/toolbar-config.js'
import { Document, Folder } from '@element-plus/icons-vue'
import Xmind from '@/icons/Xmind.vue'
import FlowChart from '@/icons/FlowChart.vue'
import { ElMessage } from 'element-plus'
import useNoteStore from '@/stores/note.js'
import { useRoute } from 'vue-router'
import formulaModule from '@wangeditor/plugin-formula'
import { Boot } from '@wangeditor/editor'

const route = useRoute()
const noteStore = useNoteStore()
const props = defineProps({
  content: {
    type: String,
    default: '',
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  initTitle: {
    type: String,
    default: '',
  },
})
const onSearch = ref(false)
const editorRef = shallowRef()
const contentHtml = ref('')
const searchOriginHtml = ref('')
const emits = defineEmits(['onSave'])
const editorHeight = ref('calc(100% - 121px)')
const searchTop = ref('56px')
const searchRight = ref('20px')
const showSearch = ref(false)
const showReplace = ref(false)
const searchKeyword = ref('')
const replaceKeyword = ref('')
const searchIndex = ref(0)
const searchTotal = ref(0)
const oldSearch = ref('')
const searchRef = ref(null)
const replaceOriginValue = ref('')
const insertNodeVisible = ref(false)
const insertNodeValue = ref('')
const title = defineModel('title')
const soLink = ref(false)

onBeforeUnmount(() => {
  const editor = editorRef.value
  if (editor == null) return
  editor.destroy()
})

onMounted(() => {
  // contentHtml.value = props.content
})

const handleCreated = (editor) => {
  editorRef.value = editor
  if (!editor.getAllMenuKeys()?.includes('insertFormula')) {
    Boot.registerModule(formulaModule)
  }
  noteStore.setWangEditorRef(editor)
  editorRef.value.on('search-replace', (val) => {
    if (val === 'search') {
      showSearch.value = !showSearch.value
      showReplace.value = false
    } else {
      showReplace.value = !showReplace.value
      showSearch.value = false
    }
    searchFocus()
  })
  editorRef.value.on('fullScreen', () => {
    searchTop.value = '0'
    searchRight.value = '-1px'
  })
  editorRef.value.on('unFullScreen', () => {
    searchTop.value = '56px'
    searchRight.value = '20px'
  })
  editorRef.value.on('linkNote', () => {
    insertNodeVisible.value = true
    insertNodeValue.value = ''
  })
  nextTick(() => {
    contentHtml.value = props.content
    if (props.disabled) {
      props.disabled && editorRef.value && editorRef.value.disable()
      return
    }
    nextTick(() => {
      editorRef.value.focus()
      editorRef.value.insertNode({ text: '' })
    })
    const container = document.getElementById('document-container')
    containerWidth.value = container ? container.offsetWidth : 0
  })
}

const searchFocus = () => {
  nextTick(() => {
    searchRef.value && searchRef.value.focus()
  })
}

const handleDestroyed = () => {
  editorRef.value = false
}

const saveContent = (value) => {
  emits('onSave', value)
}

const searchKeywords = (e) => {
  if (e.keyCode === 13) {
    e.preventDefault()
    handleSearch(searchKeyword.value)
  }
}

const replaceKeywords = (e) => {
  if (e.keyCode === 13) {
    e.preventDefault()
    handleReplace(searchKeyword.value, replaceKeyword.value)
  }
}

const handleReplace = async (searchValue, replaceValue) => {
  await nextTick()
  if (searchTotal.value === 0 || replaceValue === '') return
  replaceOriginValue.value = searchOriginHtml.value
  const newValue = searchOriginHtml.value.replaceAll(searchValue, replaceValue)
  editorRef.value.setHtml(newValue)
  searchOriginHtml.value = newValue
  searchIndex.value = 0
  saveContent(newValue)
  handleSearch(searchValue)
}

const handleSearch = (keyword, type = 'down') => {
  const regExp = new RegExp(keyword, 'g')
  if (searchOriginHtml.value === '') {
    searchOriginHtml.value = editorRef.value.getHtml()
  }
  if (oldSearch.value !== keyword) {
    searchIndex.value = 0
  }
  const lastHtml = `<span style="background-color: rgb(255, 255, 84);">${keyword}</span>`
  const currentHtml = `<span style="background-color: rgb(241, 155, 74);">${keyword}</span>`
  const matches = searchOriginHtml.value.match(regExp)
  searchTotal.value = matches ? matches.length : 0
  if (searchTotal.value) {
    if (type === 'down') {
      if (searchIndex.value >= searchTotal.value) {
        searchIndex.value = 1
      } else {
        searchIndex.value++
      }
    } else {
      if (searchIndex.value <= 1) {
        searchIndex.value = searchTotal.value
      } else {
        searchIndex.value--
      }
    }
  }
  oldSearch.value = keyword
  if (searchTotal.value === 0) {
    if (onSearch.value) {
      editorRef.value.setHtml(searchOriginHtml.value)
    }
    return
  }
  let matchIndex = 0
  const replaceHtml = searchOriginHtml.value.replace(regExp, () => {
    matchIndex++
    if (matchIndex === searchIndex.value) {
      return currentHtml
    } else {
      return lastHtml
    }
  })
  editorRef.value.setHtml(replaceHtml)
  onSearch.value = true
  searchFocus()
  nextTick(() => {
    setCurrentInWindow(currentHtml)
  })
}

const setCurrentInWindow = () => {
  const element = document.querySelector(
    'span[style="background-color: rgb(241, 155, 74);"]',
  )
  if (!element) return
  const id = getElementParentId(element)
  if (id) {
    editorRef.value.scrollToElem(id)
  }
}

const getElementParentId = (element) => {
  const id = element.getAttribute('id')
  if (id) {
    return id
  } else {
    if (element.parentElement) {
      return getElementParentId(element.parentElement)
    } else {
      return ''
    }
  }
}

const configLink = computed(() => {
  if (!soLink.value) {
    return ['editLink', 'unLink', 'viewLink']
  } else {
    return ['viewLink']
  }
})

const editorConfig = reactive({
  placeholder: '请输入内容...',
  hoverbarKeys: {
    formula: {
      menuKeys: ['editFormula'],
    },
    link: {
      menuKeys: configLink,
    },
  },
  MENU_CONF: { uploadImage: uploadImageConfig },
})


const containerWidth = ref(0)

// watch(
//   () => contentHtml.value,,
//   debounce((value) => {
//     if (onSearch.value) {
//       return
//     }
//     saveContent(value)
//   }, 800)
// )
watch(
  () => props.content,
  (value) => {
    if (value !== contentHtml.value) {
      contentHtml.value = value
    }
  },
)
const changeAction = debounce(() => {
  if (onSearch.value) {
    return
  }
  // let text = editor.getText() //text.trim() &&
  let flag = props.content !== contentHtml.value
  let flag2 = props.initTitle !== title.value
  if (flag || flag2) {
    saveContent(contentHtml.value)
  }
}, 800)


const handleChange = (editor) => {
  const nodeEntries = SlateEditor.nodes(editor, {
    match: (node) => {
      if (SlateElement.isElement(node)) {
        const { type } = node
        return type === 'link'
      }
      return false
    },
    universal: true,
  })
  let shoLink = false
  if (nodeEntries !== null) {
    for (let nodeEntry of nodeEntries) {
      const [node] = nodeEntry
      const { type, url } = node
      if (type === 'link' && (url.indexOf('file/doc') > 0
        || url.indexOf('file/mindmap') > 0
        || url.indexOf('file/drawio') > 0)) {
        shoLink = true
        if (props.content !== contentHtml.value && props.content.length > contentHtml.value.length) {
          SlateTransforms.removeNodes(editor, {
            match: (no) => {
              if (SlateElement.isElement(no)) {
                return no.type === 'link'
              }
              return false
            },
          })
        }
      }
    }
    soLink.value = shoLink
  }
  changeAction()
}

watch(
  () => containerWidth.value,
  (val) => {
    setEditorHeight(val)
  },
)

const setEditorHeight = (width) => {
  if (width < 1445) {
    editorHeight.value = 'calc(100% - 121px)'
  } else {
    editorHeight.value = 'calc(100% - 81px)'
  }
}

window.addEventListener('resize', () => {
  const container = document.getElementById('document-container')
  containerWidth.value = container ? container.offsetWidth : 0
})

const closeSearchReplace = (focus = true) => {
  showSearch.value = false
  showReplace.value = false
  if (!onSearch.value) {
    if (focus) {
      editorRef.value.focus()
    }
    return
  }
  onSearch.value = false
  if (searchOriginHtml.value !== '') {
    if (searchOriginHtml.value !== editorRef.value.getHtml()) {
      editorRef.value.setHtml(searchOriginHtml.value)
    }
    searchOriginHtml.value = ''
  }
  searchKeyword.value = ''
  replaceKeyword.value = ''
  if (focus) {
    editorRef.value.focus()
  }
}

const setDisable = (item) => {
  const newItem = { ...item }
  if (newItem.type === 1) {
    newItem.id = '1-' + newItem.id
    if (!newItem.children || newItem.children.length === 0) {
      newItem.disabled = true
    } else {
      newItem.children = newItem.children.map((child) => {
        return setDisable(child)
      })
    }
  } else {
    if (newItem.id === parseInt(route.params.docId + '')) {
      newItem.disabled = true
    }
    newItem.id = '2-' + newItem.id
  }
  return newItem
}

const selectNodeTree = computed(() => {
  return noteStore.noteData[0].children.map((item) => {
    return setDisable(item)
  })
})

const confirmInsertNode = () => {
  if (insertNodeValue.value === '') {
    ElMessage.warning('请选择笔记')
  }
  const idArr = insertNodeValue.value.split('-')
  const noteId = parseInt(idArr[idArr.length - 1])
  const { label, icon } = getNoteNameAndIcon(
    selectNodeTree.value,
    insertNodeValue.value,
  )
  const base = import.meta.env.VITE_BASE_PATH
  const filePath = getIconPath(icon)
  const node = {
    type: 'link',
    url: getHost() + base + '/file/' + filePath + '/' + noteId,
    target: '_blank',
    children: [{ color: '#409eff', text: label }],
  }
  insertNodeVisible.value = false
  nextTick(() => {
    editorRef.value.focus()
    editorRef.value.insertNode(node)
  })
}
</script>

<template>
  <div class="document-container ph-100" id="document-container">
    <Toolbar
      class="document-toolbar"
      :editor="editorRef"
      :default-config="{ toolbarKeys: ToolbarConfigs }"
      mode="default"
    />
    <el-input
      v-model="title"
      placeholder="请输入标题"
      class="title-input"
      :disabled="props.disabled"
      @input="handleChange(editorRef)"
    ></el-input>
    <Editor
      :style="{
        height: editorHeight,
        overflow: 'hidden',
        position: 'relative',
      }"
      :defaultConfig="editorConfig"
      mode="default"
      v-model="contentHtml"
      @onCreated="handleCreated"
      @onDestroyed="handleDestroyed"
      @click="closeSearchReplace(false)"
      @onChange="handleChange"
    />

    <div
      class="search-replace search flex align-items-center"
      :style="{ top: searchTop, right: searchRight }"
      v-if="showSearch"
    >
      <el-input
        ref="searchRef"
        class="w-300"
        v-model="searchKeyword"
        placeholder="Enter 查找"
        @keydown.enter="searchKeywords($event)"
      >
        <template #suffix
        ><span v-if="onSearch"
        >{{ searchIndex }}/{{ searchTotal }}</span
        ></template
        >
      </el-input>
      <el-icon
        class="margin-left-10 pointer icon-close"
        @click="handleSearch(searchKeyword)"
        v-if="searchTotal > 0"
      >
        <component is="ArrowDown"></component>
      </el-icon>
      <el-icon
        class="margin-left-10 pointer icon-close"
        @click="handleSearch(searchKeyword, 'up')"
        v-if="searchTotal > 0"
      >
        <component is="ArrowUp"></component>
      </el-icon>
      <el-icon
        class="margin-left-10 pointer icon-close"
        @click="closeSearchReplace()"
      >
        <component is="CircleClose"></component>
      </el-icon>
    </div>
    <div
      class="search-replace replace clear-float"
      :style="{ top: searchTop, right: searchRight }"
      v-if="showReplace"
    >
      <div class="float-left">
        <el-input
          ref="searchRef"
          class="w-300"
          v-model="searchKeyword"
          placeholder="查找"
          @change="handleSearch(searchKeyword)"
          @keydown.enter="searchKeywords($event)"
        >
          <template #suffix
          ><span v-if="onSearch"
          >{{ searchIndex }}/{{ searchTotal }}</span
          ></template
          >
        </el-input>
        <br />
        <el-input
          style="margin-top: 10px"
          ref="replaceRef"
          class="w-300"
          v-model="replaceKeyword"
          placeholder="替换"
          @keydown.enter="replaceKeywords($event)"
        >
        </el-input>
      </div>
      <div class="float-left margin-left-10">
        <div class="margin-top-5">
          <el-button
            link
            type="primary"
            @click="handleReplace(searchKeyword, replaceKeyword)"
          >替换
          </el-button
          >
        </div>
        <!--        <div class="margin-top-15">
                  <el-button link type="warning" @click="replaceUndo()" :disabled="replaceOriginValue === ''">撤销</el-button>
                </div>-->
      </div>
      <el-icon
        class="margin-left-10 margin-top-8 pointer icon-close"
        @click="closeSearchReplace()"
      >
        <component is="CircleClose"></component>
      </el-icon>
    </div>
  </div>
  <el-dialog
    v-model="insertNodeVisible"
    :close-on-click-modal="true"
    :title="`关联笔记`"
    center
    width="400"
  >
    <div class="h-10"></div>
    <el-tree-select
      filterable
      v-model="insertNodeValue"
      :data="selectNodeTree"
      :render-after-expand="false"
      node-key="id"
    >
      <template #default="{ node, data }">
        <span>
          <el-icon>
            <FlowChart v-if="data.icon === 'FlowChart'" />
            <Xmind v-else-if="data.icon === 'Xmind'" />
            <Document v-else-if="data.icon === 'Document'" />
            <Folder v-else />
          </el-icon>
          {{ node.label }}
        </span>
      </template>
    </el-tree-select>
    <div class="h-30"></div>
    <template #footer>
      <el-button type="primary" @click="confirmInsertNode">确 定</el-button>
      <el-button type="warning" @click="insertNodeVisible = false"
      >取 消
      </el-button
      >
    </template>
  </el-dialog>
</template>

<style scoped lang="scss">
.document-container {
  border: var(--el-border);
  min-width: 800px;

  .document-toolbar {
    border-bottom: var(--el-border);
  }

  .search-replace {
    position: absolute;
    background: #fff;
    border: var(--el-border);
    padding: 10px;
    border-top: none;

    .icon-close {
      color: var(--el-color-info);
    }
  }
}

.search-selected {
  background: #61aeee;
}

.title-input {
  :deep(.el-input__wrapper) {
    box-shadow: none !important;
    font-size: 18px;
    color: #000;
    font-weight: 600;
  }

  ::-webkit-input-placeholder {
    font-size: 14px;
  }

  :-moz-placeholder {
    font-size: 14px;
  }

  :-ms-input-placeholder {
    font-size: 14px;
  }

  :deep(.el-input__inner) {
    color: #000;
  }
}

:deep(.el-input.is-disabled .el-input__wrapper) {
  background-color: #fff !important;
}
</style>
